﻿Public Class Simulation
    ' Clé (identifiant unique de la station) : Integer
    ' Valeur (associé à la clé) : Station
    Public Stations As New Dictionary(Of Integer, Station)
    ' Variable d'incrémentation des clés
    Private IncStations As Integer = 0

    ' Trains
    Public Trains As New Dictionary(Of Integer, Train)
    Private IncTrains As Integer = 1

    ' Rails (liant les stations)
    Public Junctions As New List(Of Junction)

    ' Aiguillages
    Public RailPoints As New List(Of RailPoint)

    Public Sub Tick()
        ' Pour chaque train, on répercute l'évènement de simulation
        For Each kvp As KeyValuePair(Of Integer, Train) In Trains
            kvp.Value.Tick(Junctions, Stations, RailPoints)
        Next

        ' Puis on redessine la surface tampon d'affichage
        RefreshTrains()
    End Sub

#Region "Invalidation"
    Private Sub InvalidateJunctions()
        ' Invalidation des coordonnées de chaque rail
        For Each Junction As Junction In Junctions
            Junction.CalculateCoords(Stations)
        Next

        ' Rafraichissement du buffer des jonctions
        RefreshJunctions()
    End Sub

    Private Sub InvalidateRailPoints()
        ' Invalidation des coordonnées de chaque aiguillage
        For Each RailPoint As RailPoint In RailPoints
            ' On recalcule
            RailPoint.CalculateCoords(Stations)
        Next

        ' Rafraichissement du buffer des aiguillages
        RefreshRailPoints()
    End Sub

    Private Sub InvalidateTrains()
        ' On créé une liste des trains à supprimer (on ne peut pas supprimer au fur et à mesure dans un for each)
        Dim DeleteList As New List(Of Integer)

        ' Pour chaque train
        For Each kvp As KeyValuePair(Of Integer, Train) In Trains
            ' Invalidation des prévisions de déplacement des trains
            kvp.Value.ValidateNextStation(Junctions, RailPoints)

            ' Pour chaque aiguillage
            For Each RailPoint In RailPoints
                ' Si le train se trouve sur une voie interdite (la jonction active)
                If GetJunctionID(RailPoint.MiddleStationIndex, If(RailPoint.ActiveJunction, RailPoint.EndStation1Index, RailPoint.EndStation2Index), Junctions) = _
                   GetJunctionID(kvp.Value.PrevStation, kvp.Value.HeadingTo, Junctions) Then
                    ' On le met en attente de suppression
                    DeleteList.Add(kvp.Key)
                End If
            Next

            ' Si le train se trouve sur une voie qui n'existe pas (bizarre tout de même...)
            If (Not DeleteList.Contains(kvp.Key)) And (GetJunctionID(kvp.Value.PrevStation, kvp.Value.HeadingTo, Junctions) = -1) Then
                ' On se prépare à le supprimer
                DeleteList.Add(kvp.Key)
            End If
        Next

        ' On supprime les trains en attente de suppression
        For Each Train In DeleteList
            Trains.Remove(Train)
        Next

        ' Et on rafraichit le buffer d'affichage
        RefreshTrains()
    End Sub

    Public Sub CleanUpJunctions()
        ' Si il y a au moins une jonction dans la simulation
        If Junctions.Count > 0 Then
            ' Si la dernière jonction boucle sur elle-même (c'est possible quand on tripote trop les fonctions d'édition)
            If Junctions.Last.FromStation = Junctions.Last.ToStation Then
                ' On la supprime et on ne s'occupe pas des autres
                Junctions.Remove(Junctions.Last)
                Exit Sub
            End If

            Dim DeleteLast As Boolean = False

            ' On va vérifier si la jonction dernièrement ajoutée n'existe pas déjà dans la simulation
            For Each Junction As Junction In Junctions
                If Not Junctions.Last.Equals(Junction) Then
                    If (Junction.FromStation = Junctions.Last.FromStation And Junction.ToStation = Junctions.Last.ToStation) Or _
                       (Junction.ToStation = Junctions.Last.FromStation And Junction.FromStation = Junctions.Last.ToStation) Then
                        DeleteLast = True
                    End If
                End If
            Next

            ' Si c'est le cas, on supprime la dernière jonction
            If DeleteLast Then Junctions.Remove(Junctions.Last)
        End If
    End Sub
#End Region

#Region "Fichiers"
    Public Sub SaveToFile(FileName As String)
        ' On créé un nouveau fichier de simulation
        Dim Save As New SaveFile

        Save.Create(FileName)
        ' Et on écrit chaque section (dans l'ordre !)
        Save.WriteStations(Stations, IncStations)
        Save.WriteJunctions(Junctions)
        Save.WriteTrains(Trains, IncTrains)
        Save.WriteRailPoints(RailPoints)

        ' Puis on ferme
        Save.Close()
    End Sub

    Public Sub LoadFromFile(FileName As String)
        Dim Save As New SaveFile

        ' On se prépare à intercepter une éventuelle exception
        Try
            Save.Open(FileName)
            Save.LoadStations(Stations, IncStations)
            Save.LoadJunctions(Junctions, Stations)
            Save.LoadTrains(Trains, IncTrains)
            Save.LoadRailPoints(RailPoints)
            Save.Close()
        Catch ex As Exception
            MessageBox.Show("Erreur lors du chargement du fichier :" & vbLf & ex.Message, "Projet Micheline", MessageBoxButtons.OK, MessageBoxIcon.Error)
            ' RAZ de la simulation (on ne sait jamais...)
            Stations.Clear()
            IncStations = 0
            Trains.Clear()
            IncTrains = 1
            Junctions.Clear()
            RailPoints.Clear()
        End Try

        LinkStation = True

        ' Et on rafraichit l'affichage
        InvalidateTrains()
        InvalidateRailPoints()
        RefreshJunctions()
        RefreshStations()
    End Sub
#End Region

#Region "Ajout et suppression"
    ' Doit-on joindre la dernière station à la précédente (par des rails)
    Private LinkStation As Boolean = False

    ' On ne veut pas joindre (création d'une nouvelle ligne)
    Public Sub NewLine()
        LinkStation = False
    End Sub

    Public Sub AddStation(ByVal X As Integer, ByVal Y As Integer, ByVal Name As String)
        ' Ajoute la station à la liste
        Stations.Add(IncStations, New Station(X, Y, Name))

        ' Si il faut lier les stations
        If LinkStation And Stations.Count > 1 Then
            ' Si FocusedStationID > -1 ça veut dire qu'on ne part pas de la dernière station
            Junctions.Add(New Junction(If(FocusedStationID > -1, FocusedStationID, IncStations - 1), IncStations, Stations))
            ' La station que l'on vient d'ajouter sera la dernière et on ajoutera à partir de celle-là
            FocusedStationID = -1
        End If

        ' Et on indique qu'il faudra lier les prochaines stations entre elles
        LinkStation = True

        ' On augmente le compteur de stations
        IncStations = IncStations + 1

        ' Et on rafraichit les surfaces tampons
        RefreshJunctions()
        RefreshStations()
        InvalidateTrains()
    End Sub

    Public Sub RemoveLastStation()
        ' On retire la dernière de la liste
        Stations.Remove(IncStations - 1)
        ' Et on décrémente
        IncStations = IncStations - 1

        InvalidateTrains()
    End Sub

    Public Sub DeleteHoveredStation()
        DeleteStation(HoverStationID)

        ' La station n'existe plus donc elle ne peut pas être sous la souris
        HoverStationID = -1

        RefreshJunctions()
        RefreshStations()
    End Sub

    Private Sub DeleteStation(ID As Integer)
        ' Pour chaque aiguillage
        For Each RailPoint In RailPoints
            ' Si il s'agit d'une station composant l'aiguillage
            If ID = RailPoint.EndStation1Index Or ID = RailPoint.EndStation2Index Or ID = RailPoint.MiddleStationIndex Or ID = RailPoint.StartStationIndex Then
                ' On supprime l'ensemble de l'aiguillage
                RailPoints.Remove(RailPoint)
                ' Et on rafraichit l'affichage
                RefreshRailPoints()
                Exit For
            End If
        Next

        ' On supprime les jonctions orphelines :'(
        For Each Station In GetNeighbors(ID, Junctions, RailPoints)
            Dim JID = GetJunctionID(Station, ID, Junctions)
            If JID > -1 Then
                InvalidateRoutes(JID)
                Junctions.RemoveAt(JID)
            End If
        Next

        ' Puis on supprime simplement la station de la liste
        Stations.Remove(ID)

        InvalidateTrains()
    End Sub

    Private Sub DeleteRailPoint(ID As Integer)
        DeleteStation(RailPoints.Item(ID).MiddleStationIndex)

        RefreshJunctions()
        RefreshStations()
        RefreshRailPoints()
    End Sub

    Public Sub DeleteHoveredJunction()
        ' Pour chaque aiguillage
        For Each RailPoint In RailPoints
            ' Si la jonction fait partie d'un aiguillage
            If HoverJunctionID = GetJunctionID(RailPoint.EndStation1Index, RailPoint.MiddleStationIndex, Junctions) Or _
               HoverJunctionID = GetJunctionID(RailPoint.EndStation2Index, RailPoint.MiddleStationIndex, Junctions) Or _
               HoverJunctionID = GetJunctionID(RailPoint.MiddleStationIndex, RailPoint.StartStationIndex, Junctions) Then
                ' On supprime l'intégralité de l'aiguillage
                DeleteRailPoint(RailPoints.IndexOf(RailPoint))
                ' Puis on invalide nos aiguillages
                InvalidateRailPoints()
                ' PS : la jonction n'existe plus
                HoverJunctionID = -1
                Exit Sub
            End If
        Next

        ' On se prépare à supprimer les trains présents sur cette jonction
        Dim DeleteTrains As New List(Of Integer)

        ' Pour chaque train
        For Each kvp As KeyValuePair(Of Integer, Train) In Trains
            ' Si il se trouve sur la jonction en cours de suppression
            If (kvp.Value.PrevStation = Junctions.Item(HoverJunctionID).FromStation And kvp.Value.HeadingTo = Junctions.Item(HoverJunctionID).ToStation) Or _
               (kvp.Value.HeadingTo = Junctions.Item(HoverJunctionID).FromStation And kvp.Value.PrevStation = Junctions.Item(HoverJunctionID).ToStation) Then
                ' On le met en attente de suppression
                DeleteTrains.Add(kvp.Key)
            End If
        Next

        ' Puis on dégage ces trains en attente
        For Each Train In DeleteTrains
            Trains.Remove(Train)
        Next

        ' On supprime la jonction
        InvalidateRoutes(HoverJunctionID)
        Junctions.RemoveAt(HoverJunctionID)
        ' Elle n'existe plus donc forcémment elle n'est plus sous la souris
        HoverJunctionID = -1

        ' On rafraichit l'affichage
        RefreshJunctions()
        If DeleteTrains.Count > 0 Then RefreshTrains()
    End Sub

    Public Sub AddTrain()
        ' Si il y au moins une jonction dans la simulation (bah oui c'est mieux)
        If Junctions.Count > 0 Then
            ' On veut placer notre train de manière aléatoire, donc on va récupérer une liste des jonctions pouvant l'accueillir
            Dim PossibleJunctions As New List(Of Integer)

            ' On ajoute l'index de toutes ces jonctions dans notre liste des jonctions possibles
            For Each Junction In Junctions
                PossibleJunctions.Add(Junctions.IndexOf(Junction))
            Next

            ' On supprime les jonctions actives (interdites) d'aiguillage de la liste
            For Each RailPoint In RailPoints
                If RailPoint.ActiveJunction Then
                    PossibleJunctions.Remove(GetJunctionID(RailPoint.EndStation1Index, RailPoint.MiddleStationIndex, Junctions))
                Else
                    PossibleJunctions.Remove(GetJunctionID(RailPoint.EndStation2Index, RailPoint.MiddleStationIndex, Junctions))
                End If
            Next

            ' On shuffle notre liste et on détermine le sens de circulation du train par pile ou face
            Dim r As New Random(Now.Millisecond)
            PossibleJunctions = (From item In PossibleJunctions Order By r.Next() Select item).ToList()
            Dim sc As Boolean = CBool(r.Next(2))

            ' On détermine les stations courantes de départ et d'arrivée du train
            Dim s1 = If(sc, Junctions.Item(PossibleJunctions.Last).FromStation, Junctions.Item(PossibleJunctions.Last).ToStation)
            Dim s2 = If(sc, Junctions.Item(PossibleJunctions.Last).ToStation, Junctions.Item(PossibleJunctions.Last).FromStation)

            ' On l'instancie et on l'ajoute à la liste
            Trains.Add(IncTrains, New Train(s1, s2, r.Next(0, GetDistance(Stations.Item(s1), Stations.Item(s2))), r.Next(2, 6), -1, "T" & IncTrains))
            Trains.Item(IncTrains).ValidateNextStation(Junctions, RailPoints)
            IncTrains = IncTrains + 1

            ' Sans oublier de rafraichir l'affichage
            RefreshTrains()
        End If
    End Sub

    Private Sub InvalidateRoutes(Optional JID As Integer = -1)
        ' On liste les stations présentes sur les jonctions actives (interdites) d'aiguillage
        Dim InvalidatedStations As New List(Of Integer)

        ' Pour chaque aiguillage, on ajoute sa station interdite
        For Each RailPoint In RailPoints
            If RailPoint.ActiveJunction Then
                InvalidatedStations.Add(RailPoint.EndStation1Index)
            Else
                InvalidatedStations.Add(RailPoint.EndStation2Index)
            End If
        Next

        ' On ajoute les stations d'une éventuelle jonction supprimée
        If JID > 1 Then
            InvalidatedStations.Add(Junctions.Item(JID).FromStation)
            InvalidatedStations.Add(Junctions.Item(JID).ToStation)
        End If

        ' Faut-il passer ce train en mode parcours auto car son parcours n'est pas valide ?
        Dim SetAuto As Boolean

        ' Pour chaque train
        For Each kvp As KeyValuePair(Of Integer, Train) In Trains
            SetAuto = False

            ' Si il effectue un parcours
            If kvp.Value.Route.IsActive Then
                ' On va vérifier chaque station interdite
                For Each InvalidatedStation In InvalidatedStations
                    ' Et si elle se trouve sur le parcours du train
                    If kvp.Value.Route.Stations.Contains(InvalidatedStation) Then
                        ' Alors on va mettre ce train en mode parcours automatique
                        SetAuto = True
                        Exit For
                    End If
                Next

                ' Efface le parcours = mode auto
                If SetAuto Then kvp.Value.Route.Clear()
            End If
        Next
    End Sub

    Public Sub AddRailPoint(ByVal X As Integer, ByVal Y As Integer)
        ' Station de départ de la jonction (index)
        Dim SFrom As Integer = Junctions.Item(HoverJunctionID).FromStation
        ' Station d'arrivée
        Dim STo As Integer = Junctions.Item(HoverJunctionID).ToStation

        ' Pour chaque aiguillage
        For Each RailPoint In RailPoints
            ' Si on veut en fait activer une jonction
            If RailPoint.MiddleStationIndex = SFrom Then
                If RailPoint.EndStation1Index = STo Then
                    ' On change la jonction active
                    RailPoint.ActiveJunction = True
                    ' On rafraichit l'affichage des aiguillages
                    RefreshRailPoints()
                    ' On invalide les parcours et les trains
                    InvalidateRoutes()
                    InvalidateTrains()
                ElseIf RailPoint.EndStation2Index = STo Then
                    RailPoint.ActiveJunction = False
                    RefreshRailPoints()
                    InvalidateRoutes()
                    InvalidateTrains()
                End If
                Exit Sub
            End If
        Next

        ' On ajoute l'aiguillage
        RailPoints.Add(New RailPoint(SFrom, IncStations, STo, IncStations + 1))

        ' Mémorise l'index de l'aiguillage
        RPIndex = RailPoints.IndexOf(RailPoints.Last)

        ' Ajoute la station du milieu
        Stations.Add(IncStations, New Station(X, Y, ""))

        ' Supprime la jonction afin d'en créer deux autres ayant pour arrivée et départ la station précédemment créée
        InvalidateRoutes(HoverJunctionID)
        Junctions.Remove(Junctions.Item(HoverJunctionID))
        Junctions.Add(New Junction(SFrom, IncStations, Stations))
        Junctions.Add(New Junction(IncStations, STo, Stations))

        ' Incrémente le nombre de stations
        IncStations = IncStations + 1

        ' On focus la station en cours d'ajout qui représente une des deux jonctions d'aiguillage
        FocusedStationID = -1
        StartMovingLast()

        ' Celle-ci :
        AddStation(X, Y, "")

        ' Et on invalide les trains
        InvalidateTrains()
    End Sub
#End Region

#Region "Edition"
    ' L'index de la station en cours de déplacement
    Private MovingStationID As Integer = -1
    ' Indique si on est en train d'ajouter une sation en mode "drag"
    Private Adding As Boolean = False
    ' N'affiche pas la dernière station si le mode "drag" a collé la station en cours d'ajout à une station proche
    Private GluedStation As Integer = -1
    ' Index de l'aiguillage en cours de création
    Private RPIndex As Integer = -1

    Public Sub StartMoving()
        ' La station qui sera déplacée sera celle qui est actuellement survolée
        MovingStationID = HoverStationID
        Adding = False
    End Sub

    Public Function StartMovingLast() As Boolean
        ' Déplace la dernière station en mode glissage
        For Each RailPoint As RailPoint In RailPoints
            ' On ne veut pas déplacer l'aiguillage
            If HoverStationID = RailPoint.MiddleStationIndex Then
                Return False
            End If
        Next

        FocusedStationID = HoverStationID
        MovingStationID = IncStations
        Adding = True

        Return True
    End Function

    Public Sub Move(X As Integer, Y As Integer)
        ' Si une station est en cours de déplacement
        If MovingStationID > -1 Then
            ' Si on est en mode "drag"
            If Adding Then
                Dim P As New Point(X, Y)

                GluedStation = -1

                ' Pour chaque station, on vérifie qu'elle se trouve sous le curseur
                For Each kvp As KeyValuePair(Of Integer, Station) In Stations
                    ' Si ce n'est pas la station en cours d'ajout qui est sous le curseur et qu'elle est contenue dans le rond de la station
                    If kvp.Key <> IncStations - 1 And kvp.Value.Intersects(P) Then
                        GluedStation = kvp.Key
                        Exit For
                    End If
                Next

                For Each RailPoint As RailPoint In RailPoints
                    If GluedStation = RailPoint.MiddleStationIndex Then
                        GluedStation = -1
                        Exit For
                    End If
                Next

                ' Si une station se trouve sous le cureur
                If GluedStation > -1 Then
                    ' On définit la dernière jonction comme arrivant dans la station proche
                    Junctions.Last.ToStation = GluedStation
                    ' On actualise les données de l'aiguillage en cours de création
                    If RPIndex > -1 Then RailPoints.Item(RPIndex).EndStation2Index = GluedStation
                Else
                    ' La station d'arrivée de la dernière jonction sera la station en cours d'ajout
                    Junctions.Last.ToStation = MovingStationID
                    ' On actualise les données de l'aiguillage en cours de création
                    If RPIndex > -1 Then RailPoints.Item(RPIndex).EndStation2Index = MovingStationID
                    ' On rafraichit les coordonnées de la station en cours d'ajout afin de la faire suivre le curseur
                    Stations.Item(MovingStationID).X = X
                    Stations.Item(MovingStationID).Y = Y
                End If
            Else
                Stations.Item(MovingStationID).X = X
                Stations.Item(MovingStationID).Y = Y
            End If

            ' Invalidation des coordonnées et rafraichissement des surfaces tampons servant à l'affichage
            InvalidateJunctions()
            RefreshRailPoints()
            RefreshStations()
            RefreshTrains()
        End If
    End Sub

    Public Sub StopMoving()
        ' Si on était en mode "drag"
        If Adding Then
            ' Et qu'on a terminé d'ajouter une jonction
            If GluedStation > -1 Then
                ' On détruit la station qui était en cours d'ajout et qui ne sert plus à rien
                RemoveLastStation()
            End If
        End If

        ' Vérifie qu'on n'a pas créé une jonction inutile
        CleanUpJunctions()

        ' Si on ajoute une station, on demande son nom
        If Adding And GluedStation < 0 Then
            frmStationName.ShowDialog()
            Stations.Item(MovingStationID).Name = frmStationName.tbNomStation.Text
        End If

        ' On a terminé le déplacement
        MovingStationID = -1
        ' On est plus en mode "drag"
        Adding = False
        ' Et on ne veut plus cacher la dernière station
        GluedStation = -1
        ' On est plus en train d'ajouter un aiguillage
        RPIndex = -1
    End Sub
#End Region

#Region "Survol et sélection"
    ' Objet survolé (-1 = aucun)
    Private HoverJunctionID As Integer = -1
    Public HoverStationID As Integer = -1

    ' Station de départ du futur rail (-1 = dernière station)
    Private FocusedStationID As Integer = -1

    Public Function IsStationHovered() As Boolean
        ' Une station est-elle en cours de survol ?
        Return (HoverStationID > -1)
    End Function

    Public Function IsJunctionHovered() As Boolean
        ' Une jonction est-elle en cours de survol ?
        Return (HoverJunctionID > -1)
    End Function

    Public Sub FocusHoveredStation()
        ' La station de départ des nouvelles stations sera celle en cours de survol
        FocusedStationID = HoverStationID
    End Sub

    Public Sub HighlightHovered(ByVal X As Integer, ByVal Y As Integer)
        HoverJunctionID = -1
        HoverStationID = -1

        ' Si on est en train de déplacer une station, on n'actualise pas l'index de la station en cours de survol
        If MovingStationID > -1 Then Return

        Dim P As New Point(X, Y)

        ' Pour chaque jonction, on vérifie que le curseur est compris dans le rectangle représentant la jonction
        For Each Junction In Junctions
            If Junction.Intersects(P) Then
                ' On enregistre l'index de cette jonction
                HoverJunctionID = Junctions.IndexOf(Junction)
                Exit Sub
            End If
        Next

        ' On ne veut pas des stations d'aiguillage
        For Each RailPoint In RailPoints
            If Stations.Item(RailPoint.MiddleStationIndex).Intersects(P) Then
                HoverStationID = -1
                Exit Sub
            End If
        Next

        ' On fait la même pour chaque station
        For Each kvp As KeyValuePair(Of Integer, Station) In Stations
            If kvp.Value.Intersects(P) Then
                HoverStationID = kvp.Key
                Exit Sub
            End If
        Next
    End Sub
#End Region

#Region "Dessin"
    ' Surfaces tampons d'affchage
    Private JunctionsBuffer As New Drawing2D.GraphicsPath
    Private JunctionsFillBuffer As New Drawing2D.GraphicsPath
    Private RailPointsBuffer As New Drawing2D.GraphicsPath
    Private StationsBuffer As New Drawing2D.GraphicsPath
    Private TrainsBuffer As New Drawing2D.GraphicsPath

    ' Stylo de dessin pour les trains (rouge épais)
    Dim TrainsPen As New Pen(Color.FromArgb(255, 255, 0, 0), 2)

    ' Stylo de dessin pour les jonctions (noir épais)
    Dim JunctionsPen As New Pen(Color.FromArgb(255, 0, 0, 0), 2)

    ' Brosse de dessin des objets sélectionnés (bleu semi transparent)
    Private HoverBrush As New SolidBrush(Color.FromArgb(128, 0, 0, 255))

    Dim Font As New Font("Arial", 10)

    ' Brosse de dessin (orange semi transparent)
    Private RailPointBrush As New SolidBrush(Color.FromArgb(64, 255, 0, 0))

    Public Sub Draw(ByRef Gfx As Graphics)
        ' Active le dessin anti-aliasé (pour qu'on ne voit pas les pixels)
        Gfx.SmoothingMode = Drawing2D.SmoothingMode.AntiAlias

        ' Affiche les jonctions
        Gfx.DrawPath(JunctionsPen, JunctionsBuffer)
        JunctionsFillBuffer.FillMode = Drawing2D.FillMode.Winding
        Gfx.FillPath(Brushes.White, JunctionsFillBuffer)

        ' Les trains
        Gfx.DrawPath(TrainsPen, TrainsBuffer)

        ' Jonctions aiguillages
        Gfx.FillPath(RailPointBrush, RailPointsBuffer)

        ' Si une jonction est survolée
        If HoverJunctionID > -1 Then
            ' On la dessine
            Gfx.FillPolygon(HoverBrush, Junctions.Item(HoverJunctionID).GetPolygon())
        End If

        ' L'intérieur des stations 
        Gfx.FillPath(Brushes.White, StationsBuffer)

        ' Si une station est survolée, on l'affiche
        If HoverStationID > -1 Then
            Gfx.FillEllipse(HoverBrush, New Rectangle(Stations.Item(HoverStationID).X - 7, Stations.Item(HoverStationID).Y - 7, 14, 14))
            Dim Text = Stations.Item(HoverStationID).Name
            Gfx.DrawString(Text, Font, Brushes.Black, _
                           New Point(Stations.Item(HoverStationID).X - Gfx.MeasureString(Text, Font).Width / 2, Stations.Item(HoverStationID).Y + 10))
        End If

        ' Puis les stations en elles-mêmes
        Gfx.DrawPath(Pens.Black, StationsBuffer)
    End Sub

    Private Sub RefreshRailPoints()
        ' Rafraichissement de la surface tampon des aiguillages

        RailPointsBuffer.Reset()

        For Each RailPoint As RailPoint In RailPoints
            If RailPoint.ActiveJunction Then
                RailPointsBuffer.AddPolygon(Junctions.Item(GetJunctionID(RailPoint.MiddleStationIndex, RailPoint.EndStation1Index, Junctions)).GetPolygon())
            Else
                RailPointsBuffer.AddPolygon(Junctions.Item(GetJunctionID(RailPoint.MiddleStationIndex, RailPoint.EndStation2Index, Junctions)).GetPolygon())
            End If
        Next
    End Sub

    Private Sub RefreshJunctions()
        Dim Points As Point()

        ' RAZ des tampons d'affichage
        JunctionsBuffer.Reset()
        JunctionsFillBuffer.Reset()

        ' Efface l'intersection de l'aiguillage et remplit l'intérieur des jonctions
        For Each RailPoint As RailPoint In RailPoints
            RailPoint.CalculateCoords(Stations)
            Points = RailPoint.GetPolygon()
            JunctionsBuffer.AddLine(Points(1), Points(2))
            JunctionsBuffer.CloseFigure()
            JunctionsBuffer.AddLine(Points(3), Points(4))
            JunctionsBuffer.CloseFigure()
            JunctionsBuffer.AddLine(Points(5), Points(0))
            JunctionsBuffer.CloseFigure()
            JunctionsBuffer.AddLine(Points(0), Points(3))
            JunctionsBuffer.CloseFigure()
            JunctionsBuffer.AddLine(Points(1), Points(4))
            JunctionsBuffer.CloseFigure()
            JunctionsBuffer.AddLine(Points(2), Points(5))
            JunctionsBuffer.CloseFigure()
            JunctionsFillBuffer.AddPolygon({Points(1), Points(2), Points(3), Points(4), Points(5), Points(0)})
            JunctionsFillBuffer.AddPolygon({Points(0), Points(1), Points(4), Points(5), Points(2), Points(3)})
        Next

        ' Dessine le contour des jonctions
        For Each Junction As Junction In Junctions
            Points = Junction.GetPolygon()
            JunctionsBuffer.AddLine(Points(0), Points(1))
            JunctionsBuffer.CloseFigure()
            JunctionsBuffer.AddLine(Points(3), Points(2))
            JunctionsBuffer.CloseFigure()
            JunctionsFillBuffer.AddPolygon(Points)
        Next
    End Sub

    Private Sub RefreshStations()
        Dim RP As RailPoint

        ' RAZ tampon affichage
        StationsBuffer.Reset()

        ' Pour chaque station
        For Each kvp As KeyValuePair(Of Integer, Station) In Stations
            ' On ne veut pas afficher une station éventuellement "collée"
            If Not (GluedStation > -1 And kvp.Key = (IncStations - 1)) Then
                RP = Nothing

                ' On dégage les stations invisibles de milieu d'aiguillage
                For Each RailPoint As RailPoint In RailPoints
                    If RailPoint.MiddleStationIndex = kvp.Key Then RP = RailPoint
                Next

                If RP Is Nothing Then
                    ' Dessin de la station
                    StationsBuffer.AddEllipse(New Rectangle(kvp.Value.X - 7, kvp.Value.Y - 7, 14, 14))
                End If
            End If
        Next
    End Sub

    Public Sub RefreshTrains()
        ' Rafraichissement de la surface tampon des trains
        TrainsBuffer.Reset()
        For Each kvp As KeyValuePair(Of Integer, Train) In Trains
            kvp.Value.Draw(TrainsBuffer, Junctions, Stations)
        Next
    End Sub
#End Region

End Class
